1000 and 1 night
I wrote all this manually in 1000 and 1 night. It seems that the solution will take the same amount of time. After connecting to the server, it will ask you for a token for a binary. You need to enter the correct tokens for the requested files and you will receive a flag.
- Download: aero2020_files.zip
Recon
Zipfile with 1001 ELF 64 bit files. Files are all of the same size and seems to do the same. They ask for a Token and test if it is correct.
The service also asks for a token for a specific file.
$ ./ffeabd223de0d4eacb9a3e6e53e5448d
[?] Enter valid token: test
[-] Incorrect!
$ nc tasks.aeroctf.com 44324
Enter valid token to binary with name <82f2b308c3b01637c607ce05f52a2fed>
Token: test
Incorrect!
So the goal is to figure one binary out, find the differences between the binaries and write a script that can generate tokens for every file.
In Ghidra we can observe the following "check" function:
ulong check(void *param_1)
{
int iVar1;
undefined8 local_38;
undefined8 local_30;
undefined8 local_28;
undefined8 local_20;
int local_c;
local_38 = 0x1f6d1f1d191e6e6e;
local_30 = 0x6e20421b1e1f206b;
local_28 = 0x6a201a6f411e1e6e;
local_20 = 0x6c1c6c1c6c1d6c6d;
local_c = 0;
while (local_c < 0x20) {
*(char *)((long)param_1 + (long)local_c) =
(*(char *)((long)param_1 + (long)local_c) + 8U ^ 0x10) - 0xf;
local_c = local_c + 1;
}
iVar1 = memcmp(param_1,&local_38,0x20);
return (ulong)(iVar1 == 0);
}
It alters the input string and checks it against the encrypted string inside the binary. If we open a second binary we notice that besides the encrypted string also the parameters in the function has changed, so we end up with the following encrypt function:
def encrypt(s):
out = ""
for c in s:
chr(((ord(c) + x) ^ y) - z)
return out
And could rewrite this to a decrypt function:
def decrypt(s):
out = ""
for c in s:
chr(((ord(c) + z) ^ y) - x)
return out
Next thing we did was to find the position of the encrypted string and the parameters in the binary. We opened a hexdump of the binary and find the encrypted string and parameters by hand.
We noticed that in both binaries we checked the position was the same, so with that information we could create a solve script which would read in the file and calculates a token.
Code
from pwn import *
s = remote("tasks.aeroctf.com", 44324)
def calc_token(file):
with open(file, "rb") as f:
data = f.read()
token = ""
enc_string = data[4786:4794] + data[4796:4804] + data[4814:4822] + data[4824:4832]
x = int(data[4867].encode("hex"),16)
y = int(data[4870].encode("hex"),16)
z = 0x100 - int(data[4873].encode("hex"),16)
for i in enc_string:
token += chr(((ord(i) + z) ^ y) - x)
return token
while True:
line = s.recvline()
if "Flag" in line:
print line
break
file = line[line.find("<")+1:line.find(">")]
token = calc_token(file)
print "[+] Token for file {}: {}".format(file, token)
s.sendafter("Token:", token + "\n")
Flag
Aero{0f9e7ddd2be70f58b86f8f6589e17f182fc21c71437c2d9923fefa7ae281712b}